﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.XPath;
using ExtensionLoader;
using HttpServer;
using OpenMetaverse;
using System.IO;
using System.Xml;
using System.Net;
using WebDAVSharp;
using CableBeachMessages;
using System.Web;

namespace InventoryServer.Extensions
{
    public class AvatarService : WebDAVFrontend
    {
        WebDAVListener webdav;

        HttpRequestHandler getHandler;

        #region IExtension<InventoryServer> Members

        /// <summary>
        /// Called when the extension is starting
        /// </summary>
        public override bool Start(InventoryServer owner)
        {
            server = owner;
            server.ServiceRegistrationProvider.RegisterService(new Uri(CableBeachServices.FILESYSTEM_WEBDAV_AVATAR), CreateCapabilitiesHandler);

            //register handlers
            HttpRequestSignature signature = new HttpRequestSignature("GET", null, @"^/avatar");
            getHandler = new HttpRequestHandler(signature, GetHandler, true);
            server.HttpServer.AddHandler(getHandler);

            webdav = new WebDAVListener(server.HttpServer, @"^/avatar");
            webdav.Authentication = AuthenticationType.None;
            webdav.OnPropFind += PropFindHandler;
            webdav.OnLock += base.LockHandler;
            webdav.OnUnlock += base.UnlockHandler;

            return true;
        }

        void CreateCapabilitiesHandler(Uri identity, ref Dictionary<Uri, Uri> capabilities)
        {
            Uri[] caps = new Uri[capabilities.Count];
            capabilities.Keys.CopyTo(caps, 0);
            bool capReturned = false;

            for (int i = 0; i < caps.Length; i++)
            {
                Uri cap = caps[i];
                string capName = cap.ToString();

                switch (capName)
                {
                    case CableBeachServices.FILESYSTEM_GET_WEBDAV_AVATAR_ROOT:
                        string uri = server.HttpUri.ToString();
                        if (!uri.EndsWith("/"))
                            uri += "/";
                        uri += "avatar/";
                        uri += ServiceHelper.base64EncodeToNet(identity.ToString());
                        uri += "/";
                        capabilities[cap] = new Uri(uri);
                        capReturned = true;
                        break;
                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// Called when the extension is stopping
        /// </summary>
        public override void Stop()
        {
            //remove handlers
            server.HttpServer.RemoveHandler(getHandler);
            webdav.OnPropFind -= PropFindHandler;
            webdav.OnLock -= base.LockHandler;
            webdav.OnUnlock -= base.UnlockHandler;
        }

        #endregion

        #region Handlers

        IList<IWebDAVResource> PropFindHandler(string username_from_request, string path, DepthHeader depth)//, string absolutePath)
        {
            //path should be something like:
            // avatar/base64encodedOpenID

            string[] parts = path.Split('/');
            if (parts.Length >= 3 && parts[1] == "avatar")
            {
                string username = parts[2]; //encoded to base64
                string localPath = "/avatar";
                for (int i = 3; i < parts.Length; i++)
                {
                    string part = HttpUtility.UrlDecode(parts[i]);
                    localPath += "/" + part;
                }

                username = ServiceHelper.base64DecodeFromNet(username);

                Uri ownerIdentity;
                if (Uri.TryCreate(username, UriKind.Absolute, out ownerIdentity))
                {
                    UUID agentID = server.FilesystemProvider.IdentityToUUID(ownerIdentity);

                    InventoryCollection inventory;
                    if (server.FilesystemProvider.TryFetchFilesystem(ownerIdentity, agentID, out inventory) == BackendResponse.Success)
                    {
                        List<IWebDAVResource> davEntries = new List<IWebDAVResource>();

                        InventoryBase invObject = WebDAVHelper.PathToInventory(localPath, inventory);

                        if (invObject == null)
                        {
                            return new List<IWebDAVResource>(0);
                        }

                        path = HttpUtility.UrlPathEncode(path);
                        IWebDAVResource resource = WebDAVHelper.InventoryToDAV(path, invObject);

                        //Only add the root to response if the client wants it
                        if (depth != DepthHeader.InfinityNoRoot && depth != DepthHeader.OneNoRoot)
                        {
                            davEntries.Add(resource);
                        }

                        if (invObject is InventoryFolder && (depth == DepthHeader.One || depth == DepthHeader.Infinity))
                        {
                            InventoryFolder folder = (InventoryFolder)invObject;

                            foreach (InventoryBase child in folder.Children.Values)
                            {
                                string name = String.Empty;
                                if (child is InventoryFolder)
                                    name = ((InventoryFolder)child).Name;
                                else if (child is InventoryItem)
                                    name = ((InventoryItem)child).Name;

                                string resourcePath;
                                name = HttpUtility.UrlPathEncode(name); //encode spaces to %20 etc
                                if (path.EndsWith("/"))
                                    resourcePath = path + name;
                                else
                                    resourcePath = path + "/" + name;
                                resource = WebDAVHelper.InventoryToDAV(resourcePath, child);
                                if (resource != null)
                                    davEntries.Add(resource);
                            }
                        }

                        return davEntries;
                    }
                }
            }

            if (parts.Length == 2 || (parts.Length == 3 && parts[2] == String.Empty))
            {
                //client requested PROPFIND for avatar/
                //we need to return the avatar folder so some clients (eg. windows explorer) can access it's subfolders
                List<IWebDAVResource> davEntries = new List<IWebDAVResource>();
                davEntries.Add(new WebDAVFolder(path, DateTime.Now, DateTime.Now, DateTime.Now, false));
                //TODO: Change the DateTimes to something meaningfull
                return davEntries;
            }

            return new List<IWebDAVResource>(0);
        }

        void GetHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            //path should be something like:
            // avatar/base64encodedOpenID

            string[] parts = request.UriPath.Split('/');
            if (parts.Length >= 3 && parts[1] == "avatar")
            {
                string username = parts[2]; //encoded to base64
                string localPath = "/avatar";
                for (int i = 3; i < parts.Length; i++)
                {
                    string part = HttpUtility.UrlDecode(parts[i]);
                    localPath += "/" + part;
                }

                username = ServiceHelper.base64DecodeFromNet(username);
                Uri ownerIdentity;
                if (Uri.TryCreate(username, UriKind.Absolute, out ownerIdentity))
                {
                    UUID agentID = server.FilesystemProvider.IdentityToUUID(ownerIdentity);

                    InventoryCollection inventory;
                    if (server.FilesystemProvider.TryFetchFilesystem(ownerIdentity, agentID, out inventory) == BackendResponse.Success)
                    {
                        InventoryBase invObject = WebDAVHelper.PathToInventory(localPath, inventory);
                        if (invObject is InventoryItem)
                        {
                            InventoryItem item = (InventoryItem)invObject;

                            byte[] assetData;
                            BackendResponse storageResponse = server.AssetProvider.TryFetchData(item.AssetID, out assetData);

                            if (storageResponse == BackendResponse.Success)
                            {
                                response.Status = HttpStatusCode.OK;
                                response.Status = HttpStatusCode.OK;
                                response.ContentType = "application/octet-stream";
                                response.AddHeader("Content-Disposition", "attachment; filename=" + item.Name);
                                response.ContentLength = assetData.Length;
                                response.Body.Write(assetData, 0, assetData.Length);
                                Console.WriteLine("returning asset");
                            }
                            else if (storageResponse == BackendResponse.NotFound)
                            {
                                response.Status = HttpStatusCode.NotFound;
                            }
                            else
                            {
                                response.Status = HttpStatusCode.InternalServerError;
                            }

                        }
                        else if (invObject is InventoryFolder)
                        {
                            //TODO: return directory listing or something.
                        }
                        else //this should not happend
                        {
                            response.Status = HttpStatusCode.NotFound;
                        }
                    }
                }
            }
        }

        #endregion

    }
}
